#pragma region Copyright
/// <copyright>
/// Copyright (c) 2007 Ilia Shramko.
/// Released under the terms of the GNU General Public License.
/// </copyright>
#pragma endregion

#pragma once
#include "stdafx.h"

DECLARE_TRACE_CATEGORY( atlTraceOSHSource )
DECLARE_TRACE_CATEGORY( atlTraceOSHSourceLoader )
DECLARE_TRACE_CATEGORY( atlTraceOSHJointSource )
DECLARE_TRACE_CATEGORY( atlTraceOSHSourcePin )
DECLARE_TRACE_CATEGORY( atlTraceOSHMixerLoader )
DECLARE_TRACE_CATEGORY( atlTraceOSHMixerPlugin )
DECLARE_TRACE_CATEGORY( atlTraceOSHAP )
#define atlTraceOSHSourceConnector atlTraceOSHMixerPlugin
#define atlTracePassthroughPin atlTraceOSHMixerPlugin
#define atlTraceVMRStateController atlTraceOSHMixerPlugin

#define TRACE_BUFFER_SIZE 1024

//**********************************************************
//
//**********************************************************
class TraceLevel
{
public:
	enum
	{
		Critical = 0,
		Error = 1,
		Warning = 2,
		Info = 3,
		Verbose = 4,
	};
};


//**********************************************************
//
//**********************************************************
class LogFile
{
private:
	HANDLE _logFile;
//	CriticalSection _logCriticalSection;

public:
	LogFile::LogFile(wchar_t* logFileName)
	{
		SetLogFileName(logFileName);
	}

	virtual ~LogFile()
	{
		SetLogFileName(NULL);
	}

	void SetLogFileName(wchar_t* logFileName)
	{
		if(_logFile)
		{
			_CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_DEBUG);
			_CrtSetReportFile(_CRT_WARN, _logFile);
			::CloseHandle(_logFile);
			_logFile = NULL;
		}

		if(logFileName)
		{
			_logFile = ::CreateFile(logFileName,
				GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 
				NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
			if(_logFile)
			{
				::SetFilePointer(_logFile, 0, 0, FILE_END);
				_CrtSetReportFile(_CRT_WARN, _logFile);
				_CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE);
			}
			ATLASSERT(ATL::AtlTraceLoadSettings(NULL));
		}
	}

//
//	void Write(const char* msg, ...)
//	{
//		msg;
//#ifdef TRACEON
//		if(msg == NULL)
//			return;
//
//		enum { BUFF_LEN = 10000 };
//		char buff[BUFF_LEN];
//
//		va_list args;
//		va_start(args, msg);
//#ifdef _WIN32_WCE
//		_vsnprintf(buff, SIZEOF_ARRAY(buff), msg, args);
//#else
//		vsnprintf_s(buff, SIZEOF_ARRAY(buff), BUFF_LEN - 1, msg, args);
//#endif
//		buff[BUFF_LEN - 1] = '\0';
//		va_end(args);
//
//		//CriticalSectionLock csl(_logCriticalSection);
//
//		if(!_logFileName.empty())
//		{
//			ofstream ofs(_logFileName.c_str(), ios_base::app);
//			ofs << buff;
//		}
//#endif
//	}
//
//	void WriteLine(const char* msg, ...)
//	{
//		msg;
//#ifdef TRACEON
//		if(msg == NULL)
//			return;
//
//		enum { BUFF_LEN = 10000 };
//		char buff[BUFF_LEN];
//
//		va_list args;
//		va_start(args, msg);
//#ifdef _WIN32_WCE
//		_vsnprintf(buff, SIZEOF_ARRAY(buff), msg, args);
//#else
//		vsnprintf_s(buff, SIZEOF_ARRAY(buff), BUFF_LEN - 1, msg, args);
//#endif
//		buff[BUFF_LEN - 1] = '\0';
//		va_end(args);
//
//		//CriticalSectionLock csl(_logCriticalSection);
//
//		if(!_logFileName.empty())
//		{
//			ofstream ofs(_logFileName.c_str(), ios_base::app);
//			ofs << buff << std::endl;
//		}
//#endif
//	}
};

#ifdef _DEBUG

//**********************************************************
//
//**********************************************************
class CTraceFileLineAndTimestampInfo
{
public:
	CTraceFileLineAndTimestampInfo(const char *pszFileName, int nLineNo)
		: m_pszFileName(pszFileName), m_nLineNo(nLineNo)
	{}

	void __cdecl operator()(DWORD_PTR dwCategory, UINT nLevel, const char *pszFmt, ...) const
	{
		va_list ptr; va_start(ptr, pszFmt);
		char format[TRACE_BUFFER_SIZE];
		WriteTimestamp(format, sizeof(format)/sizeof(format[0]), pszFmt);
		ATL::CTrace::s_trace.TraceV(m_pszFileName, m_nLineNo, dwCategory, nLevel, format, ptr);
		va_end(ptr);
	}
	void __cdecl operator()(DWORD_PTR dwCategory, UINT nLevel, const wchar_t *pszFmt, ...) const
	{
		va_list ptr; va_start(ptr, pszFmt);
		wchar_t format[TRACE_BUFFER_SIZE];
		WriteTimestamp(format, sizeof(format)/sizeof(format[0]), pszFmt);
		ATL::CTrace::s_trace.TraceV(m_pszFileName, m_nLineNo, dwCategory, nLevel, format, ptr);
		va_end(ptr);
	}
	void __cdecl operator()(const char *pszFmt, ...) const
	{
		va_list ptr; va_start(ptr, pszFmt);
		char format[TRACE_BUFFER_SIZE];
		WriteTimestamp(format, sizeof(format)/sizeof(format[0]), pszFmt);
		ATL::CTrace::s_trace.TraceV(m_pszFileName, m_nLineNo, atlTraceGeneral, 0, format, ptr);
		va_end(ptr);
	}
	void __cdecl operator()(const wchar_t *pszFmt, ...) const
	{
		va_list ptr; va_start(ptr, pszFmt);
		wchar_t format[TRACE_BUFFER_SIZE];
		WriteTimestamp(format, sizeof(format)/sizeof(format[0]), pszFmt);
		ATL::CTrace::s_trace.TraceV(m_pszFileName, m_nLineNo, atlTraceGeneral, 0, format, ptr);
		va_end(ptr);
	}

	void __cdecl operator()(const BYTE* mem, size_t size) const
	{
		const BYTE* bytes = reinterpret_cast<const BYTE*>(mem);
		CStringA str;
		if(mem == NULL)
		{
			str.Append("null");
			this->operator()(str.GetString());
			return;
		}

		str.Preallocate(16*4 + 2);
		while(size >= 16)
		{
			DumpLine(bytes, 16, str);
			str.Empty();

			size -= 16;
			bytes += 16;
		}
		if(size > 0)
			DumpLine(bytes, size, str);
	}

	void __cdecl DumpLine(const BYTE* mem, size_t size, CStringA& str) const
	{
		for(size_t i = 0; i < 16; i++)
		{
			if(i < size)
			{
				int low = (mem[i] & 0xF);
				low += (low <= 9) ? '0' : ('A' - 10);

				int high = (mem[i] >> 4) & 0xF;
				high += (high <= 9) ? '0' : ('A' - 10);

				str.AppendChar(char(high));
				str.AppendChar(char(low));
				str.AppendChar(' ');
			}
			else
				str.Append("   ");
		}
		
		for(size_t i = 0; i < 16; i++)
		{
			if(i < size)
			{
				char ch = char(mem[i]);
				if(BYTE(ch) < 32) ch = ' ';
				str.AppendChar(ch);
			}
		}
		
		str.Append("\r\n");
		this->operator()(str.GetString());
	}


private:
	const char *const m_pszFileName;
	const int m_nLineNo;

	void static WriteTimestamp(char* buffer, int bufferLength, const char* restOfFormat)
	{
		//DWORD processId = ::GetCurrentProcessId();
		DWORD threadId = ::GetCurrentThreadId();

		SYSTEMTIME systemTime;
		::GetSystemTime(&systemTime);

		char timeBuff[100];
		ZeroMemory(timeBuff, SIZEOF_ARRAY(timeBuff));

		::GetTimeFormatA(NULL, 0, &systemTime, "HH':'mm':'ss", timeBuff, sizeof(timeBuff)/sizeof(timeBuff[0]));

		_snprintf_s(buffer, bufferLength, bufferLength, "[%s.%03d][%05X] %s", 
			timeBuff, systemTime.wMilliseconds, threadId, restOfFormat);
	}

	void static WriteTimestamp(wchar_t* buffer, int bufferLength, const wchar_t* restOfFormat)
	{
		//DWORD processId = ::GetCurrentProcessId();
		DWORD threadId = ::GetCurrentThreadId();

		SYSTEMTIME systemTime;
		::GetSystemTime(&systemTime);

		wchar_t timeBuff[100];
		ZeroMemory(timeBuff, SIZEOF_ARRAY(timeBuff));

		::GetTimeFormatW(NULL, 0, &systemTime, L"HH':'mm':'ss", timeBuff, sizeof(timeBuff)/sizeof(timeBuff[0]));

		_snwprintf_s(buffer, bufferLength, bufferLength, L"[%s.%03d][%05X] %s", 
			timeBuff, systemTime.wMilliseconds, threadId, restOfFormat);
	}

};

#ifndef ATLTRACE_TS
#define ATLTRACE_TS CTraceFileLineAndTimestampInfo(__FILE__, __LINE__)
#define ATLTRACE_DUMP CTraceFileLineAndTimestampInfo(__FILE__, __LINE__)
#endif

#else // !_DEBUG

#define ATLTRACE_TS __noop
#define ATLTRACE_DUMP __noop

#endif
